/**
 * @author：balthie + 10050
 * @createtime ： 2014年11月28日 下午2:29:15
 * @description TODO 一句话描述
 */
package org.balthie.demo.jdk.util.concurrent.locks.reenterlock;

import java.text.MessageFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author：balthie + 10050
 * @createtime ： 2014年11月28日 下午2:29:15
 * @description 模拟一个单线程的打印机
 * @since version 初始于版本 TODO
 * 
 *        http://blog.csdn.net/arkblue/article/details/6138598
 * 
 *        java concurrent包 ReentrantLock锁的 cas实现:
 *        http://www.cnblogs.com/MichaelPeng/archive/2010/02/12/1667947.html @1
 *        reentrant
 *        锁意味着什么呢？简单来说，它有一个与锁相关的获取计数器，如果拥有锁的某个线程再次得到锁，那么获取计数器就加1，然后锁需要被释放两次才能获得真正释放。 @2
 *        锁机制采用了操作系统 cas原语实现 @3 lock 必须在 finally
 *        块中释放。否则，如果受保护的代码将抛出异常，锁就有可能永远得不到释放！
 */
public class FairTryLockDemo extends Printer {
	// 公平锁，子线程按照线程号顺序打印

	private final Lock lock = new ReentrantLock(true);

	public static void main(String[] args) throws InterruptedException {
		FairTryLockDemo printer = new FairTryLockDemo();

		Thread[] thread = new Thread[10];

//		创建10个打印任务
		for (int i = 0; i < 10; i++) {
			thread[i] = new Thread(new PrintJob(printer));
		}

//		顺序启动10个打印任务
		for (int i = 0; i < 10; i++) {
//			每个job间隔50ms，保证启动顺序
			TimeUnit.MILLISECONDS.sleep(100);
			thread[i].start();
		}

		TimeUnit.MINUTES.sleep(1);
	}

	public void print(Object document) {

//		不停尝试获取锁
		boolean trylock = false;
		while (!trylock) {
			trylock = trylock();
		}

		try {
			Long duration = (long) (Math.random() * 10);
//			System.out.println(
//					MessageFormat.format("【{0}】printJob begin at【{1, time, HH:mm:ss:ms}】, duration 【{2,number,##}】",
//							Thread.currentThread().getName(), new Date(), duration));
			TimeUnit.SECONDS.sleep(duration);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			try {
				lock.unlock();
			} catch (Exception e) {
			}
		}

		System.out.println(MessageFormat.format("》》》》》》》【{0}】printJob end at【{1, time, HH:mm:ss:ms}】",
				Thread.currentThread().getName(), new Date()));
	}

	private boolean trylock() {
		boolean f = false;
		Long beginAt = System.currentTimeMillis();
		try {
			System.out.println(MessageFormat.format("【{0}】begin tryLock at【{1, time, HH:mm:ss:ms}】",
					Thread.currentThread().getName(), new Date()));
			f = lock.tryLock(3, TimeUnit.SECONDS);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		if (f) {
			System.out.println(MessageFormat.format("【{0}】get lock at【{1, time, HH:mm:ss:ms}】 lock [{2}]",
					Thread.currentThread().getName(), new Date(), lock.toString()));
		} else {
			System.out.println(MessageFormat.format(
					"【{0}】printJob getLock timeout at【{1, time, HH:mm:ss:ms}】, wait ms 【{2,number,##}】",
					Thread.currentThread().getName(), new Date(), System.currentTimeMillis() - beginAt));
		}
		return f;
	}

}
